package com.cit.service.impl;

import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.net.URLDecoder;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.soap.MessageFactory;
import javax.xml.soap.SOAPBody;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPEnvelope;
import javax.xml.soap.SOAPHeader;
import javax.xml.soap.SOAPMessage;
import javax.xml.soap.SOAPPart;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.sax.SAXSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

import org.w3c.dom.Comment;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;

import com.cit.service.SoapService;

/**
 * 
 * @author Lang Yu(lang.yu@hp.com)
 * @time Mar 7, 2011 12:02:26 PM
 * @file SoapServiceImpl.java
 * @package com.cit.service.impl
 * @version 1.0
 * @see SoapServiceImpl's implementation class
 */
public class SoapServiceImpl implements SoapService {
	/**
	 * Initialize the soap messages with user-defined name space
	 */
	@SuppressWarnings("unused")
	@Override
	public SOAPPart initSOAPPart(String namespace) throws Exception {
		SOAPMessage soapMessage = MessageFactory.newInstance().createMessage();
		SOAPPart soapPart = soapMessage.getSOAPPart();
		SOAPEnvelope soapEnvelope = soapPart.getEnvelope();
		SOAPHeader soapHeader = soapEnvelope.getHeader();
		SOAPBody soapBody = soapEnvelope.getBody();
		SOAPElement xsd = soapEnvelope.addNamespaceDeclaration("xsd",
				"http://www.w3.org/2001/XMLSchema");
		SOAPElement xsi = soapEnvelope.addNamespaceDeclaration("xsi",
				"http://www.w3.org/2001/XMLSchema-instance");
		SOAPElement env = soapEnvelope.addNamespaceDeclaration("SOAP-ENV",
				"http://schemas.xmlsoap.org/soap/envelope/");
		SOAPElement enc = soapEnvelope.addNamespaceDeclaration("SOAP-ENC",
				"http://schemas.xmlsoap.org/soap/encoding/");
		SOAPElement citws = soapEnvelope.addNamespaceDeclaration("", namespace);
		return soapPart;
	}

	/**
	 * Convert the Source object to a String object, it will convert the Source
	 * object's content to string in java language.
	 */
	@Override
	public String soap2String(Source source) throws Exception {
		ByteArrayOutputStream bos = new ByteArrayOutputStream();
		if (source != null) {
			Node root = null;
			if (source instanceof DOMSource) {
				root = ((DOMSource) source).getNode();
			} else if (source instanceof SAXSource) {
				InputSource inSource = ((SAXSource) source).getInputSource();
				DocumentBuilderFactory dbf = DocumentBuilderFactory
						.newInstance();
				dbf.setNamespaceAware(true);
				Document doc = dbf.newDocumentBuilder().parse(inSource);
				root = (Node) doc.getDocumentElement();
			}
			Transformer transformer = TransformerFactory.newInstance()
					.newTransformer();
			transformer.setOutputProperty(OutputKeys.INDENT, "no");
			transformer.transform(new DOMSource(root), new StreamResult(bos));
		}
		return bos.toString();
	}

	/**
	 * Convert the request data to SOAPBody object
	 */
	@Override
	public SOAPBody getSOAPBody(String data) {
		try {
			MessageFactory msgFactory = MessageFactory.newInstance();
			SOAPMessage message = msgFactory.createMessage();
			SOAPPart soapPart = message.getSOAPPart();
			byte[] buffer = data.getBytes();
			ByteArrayInputStream stream = new ByteArrayInputStream(buffer);
			StreamSource source = new StreamSource(stream);
			soapPart.setContent(source);
			SOAPBody body = soapPart.getEnvelope().getBody();
			return body;
		} catch (Exception ex) {
			// ex.printStackTrace();
			return null;
		}
	}

	/**
	 * Convert the String content into xml Document object;
	 */
	@Override
	public Document stringToDoc(String xmlStr) {
		Document doc = null;
		try {
			StringReader sr = new StringReader(xmlStr);
			InputSource is = new InputSource(sr);
			DocumentBuilderFactory factory = DocumentBuilderFactory
					.newInstance();
			DocumentBuilder builder = factory.newDocumentBuilder();
			doc = builder.parse(is);
		} catch (Exception ex) {
			ex.printStackTrace();
			doc = null;
		}
		return doc;
	}

	/**
	 * Convert the Document object into String content;
	 */
	@Override
	public String docToString(Document doc) {
		String xmlStr = "";
		try {
			TransformerFactory factory = TransformerFactory.newInstance();
			Transformer t = factory.newTransformer();
			t.setOutputProperty(OutputKeys.INDENT, "no");
			t.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
			t.setOutputProperty("encoding", "UTF-8");

			Element root = doc.getDocumentElement();
			root.setAttribute("xmlns:xsi",
					"http://www.w3.org/2001/XMLSchema-instance");
			root.setAttribute("xmlns:xsd", "http://www.w3.org/1999/XMLSchema");

			ByteArrayOutputStream bos = new ByteArrayOutputStream();
			t.transform(new DOMSource(doc), new StreamResult(bos));
			xmlStr = bos.toString();
		} catch (Exception ex) {
			ex.printStackTrace();
		}
		return xmlStr;
	}

	/**
	 * Save the document to file in the hard disk
	 */
	@Override
	public boolean saveDoc(Document doc, String filePath, boolean isNS) {
		boolean saved = true;
		try {
			if (isNS) {
				Element root = doc.getDocumentElement();
				root.setAttribute("xmlns:xsi",
						"http://www.w3.org/2001/XMLSchema-instance");
				root.setAttribute("xmlns:xsd",
						"http://www.w3.org/1999/XMLSchema");
			}
			TransformerFactory tFactory = TransformerFactory.newInstance();
			Transformer transformer = tFactory.newTransformer();
			DOMSource source = new DOMSource(doc);

			StreamResult result = new StreamResult(new File(filePath));
			transformer.transform(source, result);
		} catch (Exception e) {
			saved = false;
		}
		return saved;
	}

	/**
	 * Receive the http request's body content
	 */
	@Override
	public String getRequestData(InputStream in) throws IOException {
		StringBuilder builder = new StringBuilder();
		BufferedInputStream bIn = null;
		try {
			int b = 0;
			bIn = new BufferedInputStream(in);
			/*Reader reader = new BufferedReader(new InputStreamReader(in,
					"UTF-8"));*/
			while((b = bIn.read())!=-1){
				if(b == (int)'+'){
					builder.append("&#43;");
				}else if(b==(int)'%'){
					builder.append("&#37;");
				}else{
					builder.append((char)b);
				}
			}
			/*while ((n = reader.read(buffer)) != -1) {
				writer.write(buffer, 0, n);
			}*/
			
		} catch (Exception ex) {
			ex.printStackTrace();
		} finally {
			in.close();
			if( bIn != null ){
				try{
					bIn.close();
				}catch(Exception ex){
					ex.printStackTrace();
				}
			}
		}
		return URLDecoder.decode(builder.toString(), "UTF-8");
	}

	/**
	 * Remove the W3C's name space
	 */
	@Override
	public Document noW3cDoc(Document orgDoc, String namespace) {
		Document resultDoc = null;
		try {
			resultDoc = DocumentBuilderFactory.newInstance()
					.newDocumentBuilder().newDocument();
			// Copy the document content with no name space
			Element orgRoot = orgDoc.getDocumentElement();
			Element targetRoot = resultDoc.createElement(orgRoot.getNodeName());
			NodeList orgNodeList = orgRoot.getChildNodes();
			for (int i = 0; i < orgNodeList.getLength(); i++) {
				Node node = orgNodeList.item(i);
				if (node.getNodeType() != Element.TEXT_NODE) {
					if (node.getNodeType() == Element.COMMENT_NODE) {
						//Comment subNode = resultDoc.createComment(orgNodeList
								//.item(i).getTextContent());
						//targetRoot.appendChild(subNode);
					} else {
						Element subNode = resultDoc.createElement(orgNodeList
								.item(i).getNodeName());
						subNode.setTextContent(orgNodeList.item(i)
								.getTextContent());
						targetRoot.appendChild(subNode);
					}
				}
			}
			resultDoc.appendChild(targetRoot);
		} catch (Exception ex) {
			ex.printStackTrace();
			resultDoc = null;
		}
		return resultDoc;
	}

	@Override
	public Document convertRoot(Document orgDoc, String namespace,
			String contextName, String servletType) {
		Document resultDoc = null;
		try {
			resultDoc = DocumentBuilderFactory.newInstance()
					.newDocumentBuilder().newDocument();
			Element orgRoot = orgDoc.getDocumentElement();
			Element targetRoot = resultDoc.createElement(contextName + "_"
					+ servletType + "_" + orgRoot.getNodeName());
			// 需要拷贝根节点属性
			NamedNodeMap rootAttrs = orgRoot.getAttributes();
			for (int j = 0; j < rootAttrs.getLength(); j++) {
				Node attr = rootAttrs.item(j);
				targetRoot
						.setAttribute(attr.getNodeName(), attr.getNodeValue());
			}
			// 拷贝子节点
			NodeList orgNodeList = orgRoot.getChildNodes();
			for (int i = 0; i < orgNodeList.getLength(); i++) {
				Node node = orgNodeList.item(i);
				if (node.getNodeType() != Element.TEXT_NODE) {
					if (node.getNodeType() == Element.COMMENT_NODE) {
						Comment subNode = resultDoc.createComment(orgNodeList
								.item(i).getTextContent());
						targetRoot.appendChild(subNode);
					} else {
						// 拷贝子节点值
						Element subNode = resultDoc.createElement(orgNodeList
								.item(i).getNodeName());
						subNode.setTextContent(orgNodeList.item(i)
								.getTextContent());
						NamedNodeMap attrs = orgNodeList.item(i)
								.getAttributes();
						// 拷贝子节点每个节点的属性
						for (int j = 0; j < attrs.getLength(); j++) {
							Node attr = attrs.item(j);
							subNode.setAttribute(attr.getNodeName(), attr
									.getNodeValue());
						}
						targetRoot.appendChild(subNode);
					}
				}
			}
			resultDoc.appendChild(targetRoot);
		} catch (Exception ex) {
			ex.printStackTrace();
			resultDoc = null;
		}
		return resultDoc;
	}
}
